#include <iostream>
#include <string>
#include <pthread.h> 
 
using namespace std;

class threaded_class
{

	public:
		threaded_class() : m_stoprequested(false), m_running(false)
		{
			pthread_mutex_init(&m_mutex);
		}

		~threaded_class()
		{
			pthread_mutex_destroy(&m_mutex);
		}

		// Create the thread and start work
		// Note 1
		void go() 
		{
			assert(m_running == false);
			m_running = true;
			pthread_create(&m_thread, 0, &threaded_class::start_thread, this);
		}

		void stop() // Note 2
		{
			assert(m_running == true);
			m_running = false;
			m_stoprequested = true;
			pthread_join(&m_thread, 0);
		}

		int get_fibonacci_value(int which)
		{
			pthread_mutex_lock(&m_mutex); // Note 3 
			int value = m_fibonacci_values.get(which); // Note 4 
			pthread_mutex_unlock(&m_mutex);
			return value;
		}
 
		private:
			volatile bool m_stoprequested; // Note 5
			volatile bool m_running;
			pthread_mutex_t m_mutex; // Variable declarations added 4/14/2010
			pthread_t m_thread;

			std::vector<int> m_fibonacci_values;

		// This is the static class function that serves as a C style function pointer
		// for the pthread_create call
		static void start_thread(void *obj)
		{
			//All we do here is call the do_work() function
			reinterpret_cast<threaded_class *>(obj)->do_work();
		}

		int fibonacci_number(int num)
		{
			switch(num)
			{
				case 0:
				case 1:
				return 1;
				default:
				return fibonacci_number(num-2) + fibonacci_number(num-1); // Correct 4/6/2010 based on comments
			};
		}    

		// Compute and save fibonacci numbers as fast as possible
		void do_work()
		{
			int iteration = 0;
			while (!m_stoprequested)
			{
				int value = fibonacci_number(iteration);
				pthread_mutex_lock(&m_mutex);
				m_fibonacci_values.push_back(value);
				pthread_mutex_unlock(&m_mutex); // Note 6
			}
		}                    
};

class Interrupt
{
	private:
	
	public:
};

class Timer
{
	private:
		Interrupt *i;

	public:	
		Timer (Interrupt *i)  
		{
			this->i = i;
		}

		void run()
		{
			while(true)
			{
				cout << "Timer" << endl;
				sleep(2);
			}
		}    
};

/*class Semaphore 
  {
  int value;
  public: 
	Semaphore(int initialValue)
    {
    value = initialValue;
    }
 void P() 
    {
    while (value <= 0 ) 
      {
      pthread_cond_wait(); //replace the wait
      }
    value--;
    }
 void V() 
   {
    value++;
    pthread_cond_signal(); // replace the notify..both must be fixed
    }
 };*/
 

 
class Console
{
	private: 
		Interrupt *i;
	
	public:
	Console (Interrupt *i)
	{
		this->i = i;
	}

	void * run (void *)
	{
		while(true)
		{
			cout << "Console" << endl;
			sleep(2);
		}
	}  
};
  
class Disc
{
	private:
		Interrupt *i;
		Memory *m;
		//Semaphore sem;

	public:
		Disc(Interrupt *i, Memory *m)
		{
			this->i = i;
			this->m = m;
		}
		
		void run()
		{
			while (true)
			{
				cout << "Disc" << endl;
				sleep(5);
			}
		}
};

class Kernel // need tons of fizing
{/*
  // Access to hardware components, including the processor
  private: 
		Interrupt hint;
		Memory mem;
        Console con;
		Timer tim;
		Disc dis;
		Processor pro;
  // Data used by the kernel
 // private ProcessList readyList;
 // private ProcessList diskList;
  // In the constructor goes initialization code
  public: 
		Kernel(Interrupt  i, Memory m, Console c, Timer t, Disk d, Processor p)
		{
		hint = i;
		mem = m;
		con = c;
		tim = t;
		dis = d;
		pro = p;
    //	readyList = new ProcessList ("Ready List");
	//	diskList = new ProcessList ("Disk List");
    //  Creates the dummy process
	//	readyList.pushBack( new ProcessDescriptor(0) );
	//	readyList.getBack().setPC(0);
		}
		
		void run(int interruptNumber)
    {
		//ProcessDescriptor aux = null;
    // This is the entry point: must check what happened
		cout << "Kernel called for int " << interruptNumber << endl;
    // save context
   // readyList.getFront().setPC(pro.getPC());
   // readyList.getFront().setReg(pro.getReg());
    switch(interruptNumber)
      {
    case 2: // HW INT timer
     // aux = readyList.popFront();
     // readyList.pushBack(aux);
     // System.err.println(tabs+"CPU now runs: "+readyList.getFront().getPID());
      break;
    case 5: // HW INT disk 
     // aux = diskList.popFront();
     // readyList.pushBack(aux);
      break;
    case 15: // HW INT console
    //  System.err.println(tabs+"Operator typed " + con.getLine());
			break;
    case 36: // SW INT read
     // aux = readyList.popFront();
     // diskList.pushBack(aux);
     // dis.roda(0,0,0);
      break;
    default:
      cout << "Unknown..." << endl;
      }
    // restore context
    //pro.setPC(readyList.getFront().getPC());
    //pro.setReg(readyList.getFront().getReg());
	//readyList.print();
	//diskList.print();
	}
*/};


// Access methods
 
class Processor 
{
	private:
		Interrupt *i;
		Memory *m;
		Console *c;
		Timer *t;
		Disc *d;
		Kernel *k;		// kernel is missing for now

		int PC;				// Program Counter
		int IR[4];			// Instruction Register
		int reg;
		int flag[3];

		static const int  Z = 0;
		static const int  E = 1; // this is weird, watch out
		static const int  L = 2;

	public:
		Processor(Interrupt *i, Memory *m, Console *c, Timer *t, Disc *d)
		{
			this->i = i;
			this->m = m;
			this->c = c;
			this->t = t;
			this->d = d;
			this->PC = 0;
			//kernel = Kernel(i,m,c,t,d,this); como eu eu this pra c?   
		}

		/*int getPc() { return this->pc; }
		
		void setPc(int i) { this->pc = i; }
		
		int getReg() { return this->reg; }
		
		void setReg(int r) { this->reg = r; }
		
		int getFlag(int index) { return this->flag[index]; }
		
		void setFlag(int index, int flag) { this->flag[index] = flag; }

		*/
		void run()
		{
			while (true)
			{
				sleep(1);
				cout << "Processor";
				/*int rd = m.read(pc++);	 

				ir[1] = rd >> 24;
				ir[2] = (rd >> 16) & 255;
				ir[3] = (rd >> 8) & 255;
				ir[4] = rd & 255;

				execute_basic_instructions();

				//int thisInt = i.get();
				
				if ( thisInt != 0)
				{
					// Call the kernel passing the interrupt number
					kernel.run(thisInt);
					// Kernel handled the last interrupt
					hint.reset(thisInt);
				}*/
			}
		}

		/*void execute_basic_instructions()
		{
			if ((IR[0]=='L') && (IR[1]=='M'))
			{
				cout << "L M " << IR[2] << IR[3] << endl;
				reg[IR[2]] = mem.read(IR[3]);
			}
			else
			if ((IR[0]=='L') && (IR[1]=='C'))
			{
				cout << "L C " << IR[2] << IR[3] << endl;;
				reg[IR[2]] = IR[3];
			}
			else
			if ((IR[0]=='W') && (IR[1]=='M'))
			{
				cout << "W M " << IR[2] << IR[3] << endl;
				mem.write(IR[3],reg[IR[2]]);
			}
			else
			if ((IR[0]=='S') && (IR[1]=='U'))
			{
				cout << "S U " << IR[2] << IR[3] << endl;
				reg[IR[2]] = reg[IR[2]] - reg[IR[3]];
			}
			else
			if ((IR[0]=='A') && (IR[1]=='D'))
			{
				cout << "A D " <<IR[2] << IR[3] << endl;
				reg[IR[2]] = reg[IR[2]] + reg[IR[3]];
			}
			else
			if ((IR[0]=='D') && (IR[1]=='E') && (IR[2]=='C'))
			{
				cout << "D E C " << IR[2] << endl;
				reg[IR[3]] = reg[IR[3]] - 1;
			}
			else
			if ((IR[0]=='I') && (IR[1]=='N') && (IR[2]=='C'))
			{
				cout << "I N C "<< IR[2] << endl;
				reg[IR[3]] = reg[IR[3]] + 1;
			}
			else
			if ((IR[0]=='C') && (IR[1]=='P'))
			{
				cout <<"C P " << IR[2] << IR[3] << endl;
				if (reg[IR[2]] == 0) flag[Z] = 1; else flag[Z] = 0;
				if (reg[IR[2]] == reg[IR[3]]) flag[E] = 1; else flag[E] = 0;
				if (reg[IR[2]] < reg[IR[3]]) flag[L] = 1; else flag[L] = 0;
			}
			else
			if ((IR[0]=='J') && (IR[1]=='P') && (IR[2]=='A'))
			{
				cout << "J P A " << IR[3] << endl;
				PC = IR[3];
			}
			else
			if ((IR[0]=='J') && (IR[1]=='P') && (IR[2]=='Z'))
			{
				cout << "J P Z " << IR[3] << endl;
				if (flag[Z] == 1)
				PC = IR[3];
			}
			else
			if ((IR[0]=='J') && (IR[1]=='P') && (IR[2]=='E'))
			{
				cout << "J P E " << IR[3] << endl;
				if (flag[E] == 1)
				PC = IR[3];
			}
			else
			if ((IR[0]=='J') && (IR[1]=='P') && (IR[2]=='L'))
			{
				cout << "J P L " << IR[3] << endl;
				if (flag[L] == 1)
				PC = IR[3];
			}
			else
			if (IR[0]=='I'&&IR[1]=='N'&&IR[2]=='T')
			{
				cout << "I N T "<< IR[3];
				kernel.run(IR[3]);
			}
			else
				cout << "? ? ? ? "<< endl;
			}*/
}; 



int main ()
{
	pthread_t	threads[6];
	Interrupt i;
	Memory m;
	Console c(&i);
	Timer t(&i);
	Disc d(&i, &m);
	Processor p(&i, &m, &c, &t, &d);
	
	//pthread_create (&threads[0], NULL, &i.run(), NULL);
	//pthread_create (&threads[1], NULL, &m.run(), NULL);
	pthread_create (&threads[2], NULL, (void * (*)(void *)) (&c.run()), NULL);
	pthread_create (&threads[3], NULL, &t.run(), NULL);
	pthread_create (&threads[4], NULL, &d.run(), NULL);
	pthread_create (&threads[5], NULL, &p.run(), NULL);
	

	return 0;
}

 
